Components › Navigation › Section top bar › Section top bar

Section top bar

canon

Composable bar with title, chips, search, dividers and spacer.

Four screens hand-roll the identity instead of SectionTopBarTitle — decision A6. Phone two-step (AD 27 Sep 2026, acrm !3997, fix/nastaveni-mobil-dvoukrok): Nastavení (SettingsLayout.tsx) below the 720px breakpoint drops the side rail and becomes ⌘K's own two-step — `/nastaveni` with no section shows only the grouped menu full-width (`.nastaveni-page.m-menu`), `/nastaveni/<sekce>` shows only that section's `SectionTopBar` (`flat`) and body (`.m-section`) with a quiet `IconButton icon="arrow-left"` (`.set-back`) as the bar's first item, wired to `navigate('/nastaveni')`. Desktop keeps rail + content side by side unchanged. Side rail at ≥720px would take ~260px on a 390px phone and clip every section's fields — the same two-step shape as the ⌘K palette's list→detail step, not a new pattern. acrm !4029 (27 Sep 2026): new app-only layer apps/web/src/ds/section-top-bar.css (home for bar rules; ds.css is over the 100 kB ratchet and must not grow) — a long name in the bar stays on one line with an ellipsis and SectionTopBarTitle carries `title` with the full name; switch labels in the bar do not wrap. acrm !4044 (27 Sep 2026): the record view switcher on person/company (Activities / Profile) is the canonical `.tchip` with chevron (RULES §8: a switcher in the bar = .tchip + menu), the bespoke `.rvsw-*` was deleted. Menu items without description text (acrm !4051). Phone layout, Attio pattern (owner 2026-09-28, acrm !4088): actions as data — primaryAction {label, icon, onClick, kind} → Fab below 720 px (round only for kind 'create'), secondaryActions (menuOnly, danger, phoneOnly) → one ⋮ bottom sheet, closeAction last; row 1 = title + ⋮, row 2 = tools (view picker left, Filtr/Řadit right). Desktop unchanged. PageHeader forwards the same props. 14 Oct 2026 (owner "schvaleno" 24b v4, proto/detail-jednotne@9600c16a2, acrm !5055; RULES §26): record detail header = ↑↓ · name · ⋮ · ✕ with NO blue primary. Rare actions stay in context (Nový obchod in Nejbližší aktivita + Obchody tab; project Aktualizace in its section); an order shows one button only for the step it waits for. Dev check 14 Oct 1440 + 390: person, company, project, order — 0 primaries in the header, the project label chip gone. Registry repaired 17 Oct 2026 (review F5): PageHeader, RecordCloseButton live in apps/web (see files), not exported by @acrm/ui; dropped from react[]. 20 Oct 2026 (ACRM-569, proto/komunikace-menu-zobrazit-vse@0b7180f3a): `SectionTopBarTitle dot` — identity by colour with no glyph (`.pipe-dot`, a 12 px circle inside the `--tile-sm` frame, CSS in apps/web/src/ds/section-top-bar-dot.css); used by a mail label list. Story: "With dot".

Used in 64 files · 56 areas · 118× — components/ListSearch, components/accounting, components/agent-desk, components/datamart, components/segment, pages/AccountingInvoiceDetailPage …

Keyboard

  • Every control in the bar is a real <button> or <input>; Tab walks identity → view controls → search → actions → primary action, which is the reading order of the bar.
  • Chips that open a menu carry aria-haspopup and aria-expanded through the control that opens them; Escape closes the menu (Popover's contract).
  • The search is a normal input: typing filters, Escape is left to the screen.
  • Inline rename: Enter commits, Escape cancels, and both stop propagating so the record's own Enter/Escape handlers do not fire as well. Blur commits only if Enter has not already done it (a latch prevents double-saving).
  • The bar has no shortcut of its own; a shortcut belongs to the action that answers to it.

ARIA

  • The bar is a plain container; its accessible structure comes from the controls inside. The identity is a heading-level text, not an <h1> — the page supplies its own heading semantics.
  • SectionTopBarChip sets aria-haspopup="menu" and aria-expanded only when it is a menu trigger (chevron / open), so a plain toggle chip is not announced as a menu.
  • The search input is named by ariaLabel; the placeholder is not a name.
  • Rename mode swaps the text for an input with an accessible name from renameAriaLabel; while renaming the input is disabled and cannot be reopened.
  • Dev-only warnings guard the semantics that matter: identity missing, onRename combined with a dropdown, icon combined with a click handler.
  • Phone ⋮: IconButton named „Další akce" with aria-haspopup="menu" / aria-expanded; its bottom sheet is titled „Další akce".
  • Filtr icon button on a phone: its visible label is hidden, so the name carries both — „Filtr, aktivní podmínky: N".

Focus

  • .tbar-pos.is-link and .tbar-mark declare their own :focus-visible ring in ds.css; everything else in the bar is a <button> and takes the foundation ring (outline: 2px solid var(--accent); outline-offset: 1px).
  • The search box shows focus through :focus-within on the wrapper (accent border) rather than a ring on the input.
  • Entering rename mode moves focus into the input and selects nothing — the caret lands where the user clicked.

Contrast

Computed from packages/ui/src/tokens/tokens.css:

  • Identity name: ink on --surface-100 = 17.59:1 light, 14.53:1 dark.
  • Chip value .v ink = 17.59:1 light; chip key .k --fg-muted = 2.85:1 light, 3.63:1 dark — below AA for text, the value carries the meaning.
  • Chip border --border-strong on --surface-100 = 1.38:1 light — hairline, below the 3:1 non-text minimum.
  • Search placeholder --fg-muted = 2.85:1 light.
  • Focus ring --accent = 4.44:1 light, 3.83:1 dark — passes 3:1.

Phone view row (acrm !4114)

  • Icon-only tools in row 2 (Seskupit, Řadit, search) have an aria-label; when Seskupit or Řadit is off its default, the label and the title carry the chosen option („Řadit: Termín") — the accent dot is decorative and never the only signal.
  • The tools open a bottom sheet (role="menu", rows menuitemradio with aria-checked); the sheet is closed by „Hotovo", the scrim or Escape.
  • Expanded search has its own aria-label and a labelled × („Zavřít"); closing clears the query.

Known limits

  • Muted keys and placeholders sit at 2.85:1 in light theme.
  • No disabled style exists for a chip or the search; a control that cannot be used has to be left out instead.
  • On a narrow screen the bar scrolls sideways: a control can be off-screen with no visual hint other than the cut edge.
  • Screens that hand-roll the identity (status star, logo trigger, colour swatch) bypass the component's guardrails, so their semantics depend on the screen.

Source: docs/section-top-bar/section-top-bar.a11y.md · GitLab